#ifndef _UI_CAIRO_H_
#define _UI_CAIRO_H_

#include "ui.h"
#include "uic_widget.h"   // Ick.  Need WidgetType for get_widget helper

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

namespace uic
{

class CairoUI;
class Color
    {
  public:
    double red, green, blue,
           alpha;
    Color () : alpha(1) {}

    Color *adjust(double adjustment);
    void set(cairo_t *cr);
    void set_adjusted(cairo_t *cr, double adjust);
    };

/**
 * All of the following must be done by a GUIWindow class & header:
 *
 * 1) define a GUIWindow-derived class and add it to the CairoUI constructor
 * 2) set the mask values of GenericEvent to match what .modifier will have
 * 3) ensure that 'button' will be set to the following #defines
 */

#define LEFT_CLICK   1
#define MIDDLE_CLICK 2
#define RIGHT_CLICK  3
#define WHEEL_UP     4
#define WHEEL_DOWN   5

class GenericEvent
    {
  public:
    int x, y;
    int width, height;
    int button;
    int key;
    int modifier;
    typedef struct { int shift, lock, control,
                         alt, num_lock, scroll_lock, meta, compose;
                   } mod_values;
    static mod_values mask;
    };

class GUIWindow
    {
    friend class CairoUI;
  public:
    virtual cairo_t *get_cairo(CairoUI *ui)=0;
    virtual void resized (CairoUI *ui) {}
    virtual void set_title (const char *str) {}

  protected:
    virtual void process_input (CairoUI *ui, int fd) {}
    virtual void process_output (CairoUI *ui, int fd) {}
    virtual void fd_exception (CairoUI *ui, int fd) {}
    virtual void perform_flush (CairoUI *ui) {}

    virtual bool activate(CairoUI *ui)=0;
    virtual bool active()=0;
    virtual void move (int new_x, int new_y) {}
    };

#ifdef CAIRO_XLIB
#   include "uic_xlib.h"
#elif defined(CAIRO_XCB)
#   include "uic_xcb.h"
#else
#   error The Cairo-based UI requires either the xlib or xcb backend.
#endif

class CairoUI : public UI
    {
  public:
    CairoUI();
    ~CairoUI();

//  UI, MCP requirements
    bool activate() { return local->activate(this); }
    bool active() { return local->active(); }

    ConfigProcessor *init_processor();
    ConfigProcessor *processor();

    void process_input (int fd) { local->process_input(this, fd); }
    void fd_exception(int fd);
    bool timer_wanted();
    void timer();
    void timer_event();
    void post_event_cleanup();

    void event(Event event);
    void event(Event ev, EventData *event_data);

    void flush () { need_flush = true; }

  public:
    Preferences *prefs;
    GUIWindow *local;
    bool need_flush;
    struct timeval timer_activate;

    bool get_color_pref(Color *color, const char *prefname);
    void apply_prefs();
    void update_geometry_prefs(const GenericEvent *ev);

    void window_mapped();
    void first_configure(GenericEvent *ev);
    void redraw(bool deferred=false);
    void configure(GenericEvent *ev);

    const char *default_window_title;
    void set_song_title();
    void draw_icon(cairo_t *cr, bool draw_mask=false,
                   Color *fg=0, Color *bg=0);

    int x, y;
    int width, height;

//  Widget-related state
  public:
    Widget  *widget_list;
    Widget  *widget_pool;
    void remove_widget_from_list(Widget **list, Widget *widget);
    void add_widget_to_list(Widget **list, Widget *widget);

    double   border_size;  // a default border size for widget layout
    unsigned int selecting_time;  // for WE_SELECTING_TIME

    Color    default_fg, default_bg,
             text_fg,
             pbar_fg,
             button_fg, button_fg1, button_fg2,
             button_bg,
             icon_fg, icon_bg;

    void fire_event(Event ev);
    void fire_event(Event ev, EventData *event_data);
    void fire_event(WidgetEvent ev);
    void update_layout();

//  Event handling
    bool playing_or_paused;
    void button_press(GenericEvent *ev);
    void button_release(GenericEvent *ev);
    void motion(GenericEvent *ev);
    void pointer_enter();
    void pointer_leave();
    bool find_widget(int x, int y, Widget **widget_return=0);
    bool in_widget(Widget *widget, int x, int y);
    Widget *focused,  // widget that is currently moused over
           *release_pending[RIGHT_CLICK+1];  // button release pending
//    void layout_widgets_internal();

  protected:
    void draw_background(cairo_t *cr);
    bool draw_widget(cairo_t *cr, Widget *widget);
    void perform_flush();
    };

}
#endif
